import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


public class CopyOfProxyServlet{

	/**
	 * 
	 */
	private static String encoding = "GBK";

	private static final long serialVersionUID = 2706343257330041717L;
	ThreadPoolExecutor threadPool = new ThreadPoolExecutor(20, 30, 3, TimeUnit. SECONDS ,
			new ArrayBlockingQueue<Runnable>(2), new ThreadPoolExecutor.DiscardOldestPolicy() );
	public static void main(String[] args) throws IOException, Exception{
		(new CopyOfProxyServlet()).start();
		String inStr =
				"GET /RDBRat-Portal/ HTTP/1.1\r\n"+
						"Accept: text/html, application/xhtml+xml, */*\r\n"+
						"Accept-Language: zh-CN\r\n"+
						"User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)\r\n"+
						"Accept-Encoding: gzip, deflate\r\n"+
						"Host: localhost:7888\r\n"+
						"Connection: Keep-Alive\r\n"+
						//						"Connection: close\r\n"+
						"\r\n";
		//		String line2 = getMsg(inStr);
		//		System.out.println(line2);
	}
	private final int POOL_SIZE=10;//单个CPU线程池大小
	/*标识数字*/
	private  int flag = 0;
	/*缓冲区大小*/
	private  int BLOCK = 4096;
	/*接受数据缓冲区*/
	private  ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);
	/*发送数据缓冲区*/
	private  ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);
	private  Selector selector;
	public void start(){
		ExecutorService executorService=Executors.newFixedThreadPool(
				Runtime.getRuntime().availableProcessors()*POOL_SIZE);
		try {
			// 打开服务器套接字通道
			ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
			// 服务器配置为非阻塞
			serverSocketChannel.configureBlocking(false);
			// 检索与此通道关联的服务器套接字
			ServerSocket serverSocket = serverSocketChannel.socket();
			// 进行服务的绑定
			serverSocket.bind(new InetSocketAddress(7888));
			// 通过open()方法找到Selector
			selector = Selector.open();
			// 注册到selector，等待连接
			serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
			System.out.println("Server Start----7888:");

			listen();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
	// 监听
	private void listen() throws IOException {
		while (true) {
			// 选择一组键，并且相应的通道已经打开
			selector.select();
			// 返回此选择器的已选择键集。
			Set<SelectionKey> selectionKeys = selector.selectedKeys();
			Iterator<SelectionKey> iterator = selectionKeys.iterator();
			Runnable run = new SelectionKeyThread(iterator);
			threadPool.execute(run);
		}
	}

	String receiveText = "";
	String sendText;
	// 处理请求
	private void handleKey(SelectionKey selectionKey) throws IOException {
		// 接受请求
		ServerSocketChannel server = null;
		SocketChannel client = null;
		int count=0;
		// 测试此键的通道是否已准备好接受新的套接字连接。
		if (selectionKey.isAcceptable()) {
			// 返回为之创建此键的通道。
			server = (ServerSocketChannel) selectionKey.channel();
			// 接受到此通道套接字的连接。
			// 此方法返回的套接字通道（如果有）将处于阻塞模式。
			client = server.accept();
			// 配置为非阻塞
			client.configureBlocking(false);
			// 注册到selector，等待连接
			client.register(selector, SelectionKey.OP_READ);
		} else if (selectionKey.isReadable()) {
			// 返回为之创建此键的通道。
			client = (SocketChannel) selectionKey.channel();
			//将缓冲区清空以备下次读取
			receivebuffer.clear();
			//读取服务器发送来的数据到缓冲区中
			count = client.read(receivebuffer);
			if (count > 0) {
				receiveText = new String( receivebuffer.array(),0,count);
				System.out.println("服务器端接受客户端数据--:"+receiveText);
				client.register(selector, SelectionKey.OP_WRITE);
			}
		} else if (selectionKey.isWritable()) {
			//将缓冲区清空以备下次写入
			sendbuffer.clear();
			// 返回为之创建此键的通道。
			client = (SocketChannel) selectionKey.channel();
			sendText="message from server--" + flag++;
			sendText = receiveText;
			//向缓冲区中输入数据
			sendbuffer.put(sendText.getBytes());
			//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
			sendbuffer.flip();
			//输出到通道
			client.write(sendbuffer);
			System.out.println("服务器端向客户端发送数据--："+sendText);
			client.register(selector, SelectionKey.OP_READ);
		}
	}
	public class SelectionKeyThread implements Runnable{
		private Socket s;
		private Socket client;
		private SocketChannel channel;
		private Charset charset = Charset.forName("utf8");// 创建GBK字符集
		Iterator<SelectionKey> iterator;
		public SelectionKeyThread(Iterator<SelectionKey> iterator)
		{
			try {
				//				s = new Socket("59.108.115.101", 7888);
				InetSocketAddress socketAddress = new InetSocketAddress(
						"www.baidu.com", 80);
				//step1:打开连接
				channel = SocketChannel.open(socketAddress);
			} catch (UnknownHostException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			this.iterator = iterator;
		}
		@Override
		public void run() {
			while (iterator.hasNext()) {
				SelectionKey selectionKey = iterator.next();
				iterator.remove();
				try {
					handleKey(selectionKey);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		String receiveText = "";
		String sendText;
		// 处理请求
		private void handleKey(SelectionKey selectionKey) throws IOException {
			// 接受请求
			ServerSocketChannel server = null;
			SocketChannel client = null;
			int count=0;
			// 测试此键的通道是否已准备好接受新的套接字连接。
			if (selectionKey.isAcceptable()) {
				// 返回为之创建此键的通道。
				server = (ServerSocketChannel) selectionKey.channel();
				// 接受到此通道套接字的连接。
				// 此方法返回的套接字通道（如果有）将处于阻塞模式。
				client = server.accept();
				// 配置为非阻塞
				client.configureBlocking(false);
				// 注册到selector，等待连接
				client.register(selector, SelectionKey.OP_READ);
				System.err.println(1);
			} else if (selectionKey.isReadable()) {
				// 返回为之创建此键的通道。
				client = (SocketChannel) selectionKey.channel();
				//将缓冲区清空以备下次读取
				receivebuffer.clear();
				//读取服务器发送来的数据到缓冲区中
				count = client.read(receivebuffer);
				if (count > 0) {
					receiveText = new String( receivebuffer.array(),0,count);
					System.out.println("服务器端接受客户端数据--:"+receiveText);
					client.register(selector, SelectionKey.OP_WRITE);
				}
				System.err.println(2);
			} else if (selectionKey.isWritable()) {
				//将缓冲区清空以备下次写入
				sendbuffer.clear();
				// 返回为之创建此键的通道。
				client = (SocketChannel) selectionKey.channel();
				sendText="message from server--" + flag++;
				sendText = receiveText;
				//向缓冲区中输入数据
				sendbuffer.put(sendText.getBytes());
				//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
				sendbuffer.flip();
				//输出到通道
				client.write(sendbuffer);
				System.out.println("服务器端向客户端发送数据--："+sendText);
				client.register(selector, SelectionKey.OP_READ);
				System.err.println(3);
			}
			System.out.println(client);
		}
	}
	/*
	 * 这里我们自己模拟读取一行，因为如果使用API中的BufferedReader时，它是读取到一个回车换行后
	 * 才返回，否则如果没有读取，则一直阻塞，直接服务器超时自动关闭为止，如果此时还使用BufferedReader
	 * 来读时，因为读到最后一行时，最后一行后不会有回车换行符，所以就会等待。如果使用服务器发送回来的
	 * 消息头里的Content-Length来截取消息体，这样就不会阻塞
	 * 
	 * contentLe 参数 如果为0时，表示读头，读时我们还是一行一行的返回；如果不为0，表示读消息体，
	 * 时我们根据消息体的长度来读完消息体后，客户端自动关闭流，这样不用先到服务器超时来关闭。
	 */
	private static String readLine(InputStream is, int contentLe) throws IOException {
		ArrayList lineByteList = new ArrayList();
		byte readByte;
		int total = 0;
		if (contentLe != 0) {
			do {
				readByte = (byte) is.read();
				lineByteList.add(Byte.valueOf(readByte));
				total++;
			} while (total < contentLe);//消息体读还未读完
		} else {
			do {
				readByte = (byte) is.read();
				lineByteList.add(Byte.valueOf(readByte));
			} while (readByte != 10);
		}

		byte[] tmpByteArr = new byte[lineByteList.size()];
		for (int i = 0; i < lineByteList.size(); i++) {
			tmpByteArr[i] = ((Byte) lineByteList.get(i)).byteValue();
		}
		lineByteList.clear();

		return new String(tmpByteArr, encoding);
	}
}
